#include "config.h"

#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <unistd.h>

#define DBG_SUBSYS S_LIBCONTROL

#include "limits.h"
#include "adt.h"
#include "sysy_lib.h"
#include "bmap.h"
#include "net_table.h"
#include "configure.h"
#include "table_proto.h"
#include "volume_proto.h"
#include "metadata.h"
#include "md_proto.h"
#include "md_map.h"
#include "net_global.h"
#include "volume_ctl.h"
#include "../storage/stor_rpc.h"
#include "volume.h"
#include "job_dock.h"
#include "ylog.h"
#include "dbg.h"

#define VOLUME_IO_PATH       SHM_ROOT"/volume/io/"

static void __volume_proto_analysis_get(analy_t *analysis,
                uint32_t *readps, uint32_t *writeps, uint32_t *readbwps, uint32_t *writebwps)
{
        int prev = (analysis->cur + ANALY_AVG_UPDATE_COUNT - 1) % ANALY_AVG_UPDATE_COUNT;

        *readps = analysis->readps[prev];
        *writeps = analysis->writeps[prev];
        *readbwps = analysis->readbwps[prev];
        *writebwps = analysis->writebwps[prev];
}

static void __volume_proto_analysis_update(analy_t *analysis, int len, int op, time_t now)
{
        int next = 0, cnt, i;

        if (op == __OP_READ) {
                analysis->read_count++;
                analysis->read_bytes += len;
        } else if (op == __OP_WRITE) {
                analysis->write_count++;
                analysis->write_bytes += len;
        } else {
                YASSERT(0);
        }

        if (analysis->last == 0) {
                analysis->cur = 0;
                if (op == __OP_READ) {
                        analysis->readps[0] = 1;
                        analysis->readbwps[0] = len;
                } else {
                        analysis->writeps[0] = 1;
                        analysis->writebwps[0] = len;
                }
                analysis->last = now;
        } else if (now > analysis->last) {
                cnt = now - analysis->last;
                for (i = 1; i <= cnt; i++) {
                        next = (analysis->cur + i) % ANALY_AVG_UPDATE_COUNT;
                        analysis->readps[next] = 0;
                        analysis->readbwps[next] = 0;
                        analysis->writeps[next] = 0;
                        analysis->writebwps[next] = 0;
                }

                YASSERT(next >= 0 && next < ANALY_AVG_UPDATE_COUNT);
                analysis->cur = next;
                if (op == __OP_READ) {
                        analysis->readps[next] = 1;
                        analysis->readbwps[next] = len;
                } else {
                        analysis->writeps[next] = 1;
                        analysis->writebwps[next] = len;
                }
                analysis->last = now;
        } else {
                if (op == __OP_READ) {
                        analysis->readps[analysis->cur] += 1;
                        analysis->readbwps[analysis->cur] += len;
                } else {
                        analysis->writeps[analysis->cur] += 1;
                        analysis->writebwps[analysis->cur] += len;
                }
        }
}

static void __volume_proto_analysis_dump(analy_t *analysis, char *buf, time_t now)
{
        uint32_t readps, writeps, readbwps, writebwps;

        __volume_proto_analysis_get(analysis, &readps, &writeps, &readbwps, &writebwps);

        if (unlikely(now - analysis->last_output > 2)) {
                snprintf(buf, MAX_INFO_LEN, "read: %llu\n"
                                "read_bytes: %llu\n"
                                "write: %llu\n"
                                "write_bytes: %llu\n"
                                "\n"
                                "readps:%u\n"
                                "readbwps:%u\n"
                                "writeps:%u\n"
                                "writebwps:%u\n"
                                "\n"
                                "time:%ld\n",

                                (LLU)analysis->read_count,
                                (LLU)analysis->read_bytes,
                                (LLU)analysis->write_count,
                                (LLU)analysis->write_bytes,

                                readps,
                                readbwps,
                                writeps,
                                writebwps,

                                now);
                analysis->last_output = now;
        }
}

int volume_proto_analysis_record(volume_proto_t *volume_proto, int len, int op)
{
        int ret;
        char buf[MAX_INFO_LEN], path[MAX_PATH_LEN];
        fileid_t *fileid; 
        analy_t *analysis;
        time_t now;

        now = gettime();

        buf[0] = '\0';
        fileid = &volume_proto->chkid;
        analysis = &volume_proto->analysis;

        __volume_proto_analysis_update(analysis, len, op, now);
        __volume_proto_analysis_dump(analysis, buf, now);

        if (unlikely(strlen(buf))) {
                snprintf(path, MAX_PATH_LEN, VOLUME_IO_PATH"/%s/"CHKID_FORMAT"",
                                volume_proto->table1.pool, CHKID_ARG(fileid));
                ret = _set_text(path, buf, strlen(buf) + 1, O_CREAT | O_TRUNC);
                if (unlikely(ret))
                        GOTO(err_ret, ret);
        }

        return 0;
err_ret:
        return ret;
}

int volume_proto_analysis_init(volume_proto_t *volume_proto)
{
        analy_t *analysis;

        analysis = &volume_proto->analysis;
        memset(analysis, 0x0, sizeof(*analysis));

        return 0;
}

void volume_proto_analysis_destroy(volume_proto_t *volume_proto)
{
        char path[MAX_PATH_LEN];
        fileid_t *fileid;

        fileid = &volume_proto->chkid;
        snprintf(path, MAX_PATH_LEN, VOLUME_IO_PATH"/"CHKID_FORMAT"",
                        CHKID_ARG(fileid));

        unlink(path);
}
